from selenium import webdriver
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.select import Select
from selenium.common.exceptions import InvalidSelectorException
from typing import List, Union, Any
import time


def to_element(element: WebElement) -> 'Element':
  return Element(element._parent, element._id, element._w3c)


class Attrs:
  href: str
  src: str
  style: str
  class_: str
  id: str

  def __init__(self, el: 'Element'):
    super().__setattr__('_el', el)

  def __getattr__(self, name):
    return self[name]

  def __setattr__(self, name, value):
    self[name] = value

  def __getitem__(self, name):
    return self._el.get_attribute(name)

  def __setitem__(self, name, value):
    self._el.run('(e, name, value)=>e[name] = value', name, value)


class Element(WebElement):

  def find(self, css_selector: str) -> 'Element':
    try:
      return to_element(self.find_element_by_css_selector(css_selector))
    except Exception:
      return None

  def find_all(self, css_selector: str) -> List['Element']:
    return [to_element(item) for item in self.find_elements_by_css_selector(css_selector)]

  def run(self, js_fun: str, *args) -> Union[Any, str, dict, 'Element', List['Element']]:
    script = 'return (%s)(...arguments)' % js_fun
    response = self.parent.execute_script(script, self, *args)
    return response

  def checked(self) -> 'Element':
    """单选、多选选中"""
    self.run('e=>e.checked=true')
    return self

  def unchecked(self) -> 'Element':
    """单选、多选取消选中"""
    self.run('e=>e.checked=false')
    return self

  @property
  def select(self) -> Select:
    """select操作"""
    return Select(self)

  def click(self) -> 'Element':
    super().click()
    return self

  @property
  def attrs(self) -> Attrs:
    return Attrs(self)

  @property
  def html(self) -> str:
    return self.attrs['innerHTML']

  @property
  def outhtml(self) -> str:
    return self.attrs['outerHTML']

  @property
  def text(self) -> str:
    return self.attrs['innerText']

  @property
  def tagname(self) -> str:
    return self.attrs['tagName'].lower()

  def clear(self):
    super().clear()
    return self


class Browser(webdriver.Chrome):

  def get(self, url: str) -> 'Browser':
    super().get(url)
    return self

  def find(self, css_selector: str) -> Element:
    try:
      return to_element(self.find_element_by_css_selector(css_selector))
    except Exception:
      return None

  def find_all(self, css_selector: str) -> List[Element]:
    return [to_element(item) for item in self.find_elements_by_css_selector(css_selector)]

  def run(self, js_fun: str, *args) -> Union[Any, str, dict, Element, List[Element]]:
    script = 'return (%s)(...arguments)' % js_fun
    response = self.execute_script(script, *args)
    return response

  @property
  def html(self) -> str:
    return self.page_source

  @property
  def url(self) -> str:
    return self.current_url

  @url.setter
  def url(self, url) -> str:
    self.get(url)

  def sleep(self, n: int) -> 'Browser':
    time.sleep(n)
    return self
